﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Crocodile.Debugging;
using Crocodile.LangUtils;
using Crocodile.SimplexLanguage.Tree;



namespace Crocodile.SimplexLanguage.Parser
{
    /// <summary>
    /// Parser of simplex language text.
    /// </summary>
    /// <stereotype>service</stereotype>
    public class SimplexLanguageParser
    {

        public Predicate<string> FlatPredicate;


        public SimplexProject ParseDirectory (DirectoryInfo dir,
                                              string fileNamePattern)
        {
            var simplexProject = new SimplexProject();
            var files =
                dir.EnumerateFiles(fileNamePattern);
            foreach (var file in files)
            {
                var simplexFile =
                    ParseFile(file.Name, File.ReadLines(file.FullName));
                simplexProject.Files.Add(simplexFile);
            }
            return simplexProject;
        }


        public SimplexFile ParseFile (string fileName,
                                      IEnumerable<string> strings)
        {
            var simpf = new SimplexFile(fileName);

            var sequence =
                strings
                    .Select(str => str.TrimEnd())
                    .Select((str,nr0) => new SimplexLine(nr0+1,str))
                    .Where(line => !line.IsEmpty())
                    .Concat(Enumerable.Repeat(EOF, 1));

            var iterator = sequence.GetEnumerator();
            iterator.MoveNext() ._assert_true_();

            ProcessTree(fileName, iterator, -1, simpf.Content);

            return simpf;
        }


        private void ProcessTree(string fileName, IEnumerator<SimplexLine> iterator,int parentOffset,IList<SimplexEntity> sink)
        {
            while (iterator.Current.Offset > parentOffset)
            {
                var entity = MakeEntity(fileName, iterator.Current);
                sink.Add(entity);

                iterator.MoveNext()  ._assert_true_();

                bool flat = FlatPredicate != null && FlatPredicate(entity.Line);
                if (flat)
                    ProcessFlat(fileName, iterator,entity.Offset,entity.Content);
                else
                    ProcessTree(fileName, iterator,entity.Offset,entity.Content);
            }
        }


        private void ProcessFlat(string fileName, IEnumerator<SimplexLine> iterator, int parentOffset, IList<SimplexEntity> sink)
        {
            var lines = new List<SimplexLine>();

            while (iterator.Current.Offset > parentOffset)
            {
                lines.Add(iterator.Current);
                iterator.MoveNext()  ._assert_true_();
            }

            int alignOffset = lines.Min(line => line.Offset);

            var entities =
                lines
                    .Select(line => AlignLine(line, alignOffset))
                    .Select(line => MakeEntity(fileName, line));

            foreach (var entity in entities)
                sink.Add(entity);
        }


        private static SimplexEntity MakeEntity(string fileName, SimplexLine line)
        {
            return new SimplexEntity 
                        {
                            FileName = fileName, 
                            LineNr = line.Nr, 
                            Offset = line.Offset, 
                            Line = line.Str
                        };
        }


        private static SimplexLine AlignLine(SimplexLine line, int alignOffset)
        {
            return line.Offset <= alignOffset
                       ? line
                       : new SimplexLine(line.Nr, alignOffset, line.Str.InsertSpaces(line.Offset - alignOffset));
        }


        private static readonly SimplexLine EOF = new SimplexLine(int.MaxValue, int.MinValue, string.Empty);

    }
}
